package com.line.tools.util;

import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.SOAPException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XmlUtils {
	private static Logger log = LoggerFactory.getLogger(XmlUtils.class);

	/**
	 * 采用HTTP URLConnection进行直接请求
	 * 
	 * @param urlPath
	 *            请求URL
	 * @param soap12Body
	 *            请求主体
	 * @param connectTimeout
	 *            链接超时
	 * @param readTimeout
	 *            读取超时
	 * @return 响应结果
	 * @throws IOException
	 *             IO异常
	 * @throws ParserConfigurationException
	 *             配置异常
	 * @throws SAXException
	 *             SAX异常
	 * @throws SOAPException
	 *             SOAP异常
	 * @throws JDOMException
	 *             JDOM异常
	 */
	public static String soap12Request(String urlPath, String requestBody, Integer connectTimeout, Integer readTimeout, String method) {
		long start = System.currentTimeMillis();
		StringBuffer requestXml = new StringBuffer("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
		requestXml.append(
				"<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">");
		requestXml.append("<soap12:Body>");
		requestXml.append(requestBody);
		requestXml.append("</soap12:Body>");
		requestXml.append("</soap12:Envelope>");
		try {
			URL url = new URL(urlPath);
			HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
			if (connectTimeout == null) {
				connectTimeout = 20000;
			}
			if (readTimeout == null) {
				readTimeout = 20000;
			}
			httpURLConnection.setRequestProperty("Content-Length", requestXml.length() + ""); // 设置请求长度
			httpURLConnection.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8"); // 设置请求体内容格式
			httpURLConnection.setRequestMethod("POST"); // 设置请求方式
			httpURLConnection.setDoInput(true);
			httpURLConnection.setDoOutput(true);
			httpURLConnection.setUseCaches(false);
			httpURLConnection.setConnectTimeout(connectTimeout * 1000); // 链接超时
			httpURLConnection.setReadTimeout(readTimeout * 1000); // 读取超时
			httpURLConnection.connect(); // 打开通道
			OutputStream out = httpURLConnection.getOutputStream(); // 获取输出流对象
			out.write(requestXml.toString().getBytes("UTF-8")); // 将要提交服务器的SOAP请求字符流写入输出流
			out.flush();
			out.close();

			String line, response = "";
			BufferedReader in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8"));
			while ((line = in.readLine()) != null) {
				response += line;
			}
			in.close();
			long end = System.currentTimeMillis();
			long msTime = end - start;
			double sTime = DecimalUtils.conversionTime(msTime, 1000);
			int code = httpURLConnection.getResponseCode();
			if (StringUtils.isBlank(response)) {
				return null;
			}
			// 只需要获取body信息即可
			response = response.substring(response.indexOf("soap:Body") + ("soap:Body".length() + 1), response.lastIndexOf("soap:Body") - 2);
			log.info("[{}] url:{}; responseCode:{}; cost:{}ms,{}s; requestXml:{} responseXml:{}", method, urlPath, code, msTime, sTime,
					escapeString(requestBody), escapeString(response));
			return response;
		} catch (Exception e) {
			long end = System.currentTimeMillis();
			long msTime = end - start;
			double sTime = DecimalUtils.conversionTime(msTime, 1000);
			log.error("[{}] url:{}; cost:{}ms,{}s; requestXml:{}; exception:{}; ", method, urlPath, msTime, sTime, requestXml, e);
			return null;
		}
	}

	public static String escapeString(String value) {
		String escapeString = "";
		if (value != null) {
			escapeString = value.replaceAll("&lt;", "<");
			escapeString = escapeString.replaceAll("&gt;", ">");
			Pattern p = Pattern.compile("\t|\r|\n");
			Matcher m = p.matcher(escapeString);
			escapeString = m.replaceAll("");
		}
		return escapeString;
	}

	/**
	 * JavaBean转换成xml
	 * 
	 * @param obj
	 * @param encoding
	 * @return
	 */
	public static String convertToXml(Object obj, String encoding) {
		String result = null;
		try {

			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
			// marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
			// marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			result = writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

	/**
	 * xml转换成JavaBean
	 * 
	 * @param xml
	 * @param c
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T converyToJavaBean(String xml, Class<T> c) {
		try {
			JAXBContext context = JAXBContext.newInstance(c);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			T t = (T) unmarshaller.unmarshal(new StringReader(xml));
			return t;
		} catch (Exception e) {
			log.error("Convert exception:", e);
			return null;
		}
	}

	public static Map<String, String> decodeXml(String xml) {
		Map<String, String> retMap = new HashMap<String, String>();
		try {
			StringReader read = new StringReader(xml);
			// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
			InputSource source = new InputSource(read);
			// 创建一个新的SAXBuilder
			SAXBuilder sb = new SAXBuilder();
			// 通过输入源构造一个Document
			Document doc = sb.build(source);
			Element root = doc.getRootElement();// 指向根节点
			List<Element> es = root.getChildren();
			if (es != null && es.size() != 0) {
				for (Element element : es) {
					retMap.put(element.getName(), element.getValue());
				}
			}
		} catch (Exception e) {
			log.error("将XML转换为Map失败，失败异常：", e);
		}
		return retMap;
	}

	public static org.jdom2.Document stringToXML(String xmlString) {
		try {
			StringReader read = new StringReader(xmlString);
			// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
			InputSource source = new InputSource(read);
			// 创建一个新的SAXBuilder
			SAXBuilder sb = new SAXBuilder();
			// 通过输入源构造一个Document
			org.jdom2.Document doc = sb.build(source);
			return doc;
		} catch (Exception e) {
			return null;
		}
	}
}
